let JSONbig=require("json-bigint") ;
let request=require("request");
let socket=require('socket.io-client');
let repl = require("repl");
let readline = require("readline");

let rl = readline.createInterface({
    input:process.stdin,
    output:process.stdout
});
let helloNaive;

stack = {
    get:()=>{
        return {
            open:(url)=>{
                console.log(`open打开了${url}页面`);
                rl.question(`\n输入要测试的naive方法,有input,redirect,popup,store,retrieve,info,finish:`,async answer =>{
                    await getAgent(helloNaive).invoke(answer,{});
                })
            },
            popup:(url)=>{
                console.log(`popup打开了${url}页面`);
                rl.question(`\n输入要测试的naive方法,有input,redirect,popup,store,retrieve,info,finish:`,async answer =>{
                    await getAgent(helloNaive).invoke(answer,{});
                })
            },
            input:async(url,document)=>{
                console.log(`打开了${url}页面,并在此页面点击事件里执行下一步,执行完后会自动关闭当前页面,返回上一层页面`);
                //在此页面点击事件里执行下一步
                rl.question("\n请输入需要传到服务器的json数据,比如{\"name\":\"张三\"}:",async(answer)=>{
                    try{
                        if (answer) {
                            document.next(JSON.parse(answer));
                        }else {
                            document.next({name:"张三"});
                        }
                    }catch (e){
                        document.next({name:"张三"});
                    }
                });
                return {
                    finish:()=>{
                        console.log(`关闭${url}页面`);
                        rl.question(`\n输入要测试的naive方法,有input,redirect,popup,store,retrieve,info,finish:`,async answer =>{
                            await getAgent(helloNaive).invoke(answer,{});
                        })
                    },
                }
            },
        }
    }
};
let db = {};
sp = {
    open:()=>{},
    get:(key)=>{
        return db[key]||"空的东西"
    },
    put:(key,value)=>{
        db[key] = value;
        console.log(`向db里存一个键值对${key}:${value}`);
    },
};

function alert(data) {
    console.log(`alert 一段服务端返回的字符串:${data}`);
    rl.question(`\n输入要测试的naive方法,有input,redirect,popup,store,retrieve,info,finish:`,async answer =>{
        await getAgent(helloNaive).invoke(answer,{});
    })
}
function doRequest(url, post) { // GET/POST json and retrieve json.
    console.log(`url:${url}, sending:`,post);
    if (typeof post != "undefined")
        return new Promise(function (resolve, reject) {
            request({
                url, method:"POST", json:true,
                header: {"Content-type": "application/json; charset=UTF-8"},
                body: post
            }, function (error, res, body) {
                if (!error && res.statusCode == 200) {
                    console.log('answer:',body);
                    resolve(body);
                } else {
                    resolve({error});
                }
            });
        });
    else
        return new Promise(function (resolve, reject) {
            request(url, function (error, res, body) {
                if (!error && res.statusCode == 200) {
                    let json=JSONbig.parse(body);
                    console.log('answer:',json);
                    resolve(json);
                } else {
                    resolve({error});
                }
            });
        });
}
function initNaive(opt) {
    let baseServer = `http://127.0.0.1:1244`;//opt.baseServerApi||
    let post = doRequest;

    let ServerAgent = {
        invoke: async (api, data) => await interactCallServer("logic", api, data),
    };
    let getAgent = function (naive) {
        return {
            invoke: async (api, data) => await interactCallServer(`naive/${naive}`, `invoke/${api}`, data),
        }
    };

    let interactCallServer = async function (type, api, data) {
        let response = await post(`${baseServer}/${type}/${api}`, data);
        return await interactionTypes[response.verb](response.next, response.args);
    };

    let interactionTypes = {
        "wsconnect": async (next, naiveName) => {
            let targetNaive = await getNaive(naiveName);
            return await interactCallServer("interact", next, targetNaive.address);
        },
        "redirect": async(next, pck) => {
            let {url} = pck;
            stack.get().open(url, pck);
            return await interactCallServer("interact", next,{});
        },
        "popup": async(next, pck) => {
            let {url} = pck;
            stack.get().popup(url, pck);
            return await interactCallServer("interact", next,{});
        },
        "retrieve": async (next, list) => {
            sp.open("zk");
            let result = {};
            for (let key of list)
                result[key] = sp.get(key);
            return await interactCallServer("interact", next, result);
        },
        "info": async(next, result) => {
            alert(result);
            return await interactCallServer("interact", next, {});
            // return result;
        },
        "finish": (next, result) => {console.log("流程结束");return result},
        "store": async (next, what) => {
            sp.open("zk");
            for (let key in what) {
                if (what.hasOwnProperty(key)) {
                    sp.put(key, `${typeof what[key] == "object" ? JSON.stringify(what[key]) : what[key]}`);
                }
            }
            return await interactCallServer("interact", next, {success: true});
        },
        "input": async (next, pck) => {
            let {url} = pck;
            return new Promise(async (res, rej) => {
                let newWindow = await stack.get().input(url, {
                    next: async (result) => {
                        let response = await post(`${baseServer}/interact/${next}`, result);
                        res(await interactionTypes[response.verb](response.next, response.args));
                        newWindow.finish();
                    },
                    naive: pck
                });
            });
        }
    };

    naives = {};
    let getNaive =
        (naiveName) =>
            naives[naiveName].ready.then(() => naives[naiveName].naive);
    let genNaive = (naiveName)=> {
        let uuid = "invalid";
        let cb = {};
        console.log(`generating naive:${naiveName}, connecting websocket...`);
        let ready = new Promise((res, rej) => {
            let socket = io();
            socket.on("id", (id) => {
                console.log(`naive ${naiveName} ready, id:${id}`);
                uuid = id;
                naive.address = uuid;
                res();
            });
            socket.on("method", (data) => {
                console.log(`naive ${naiveName} being invoked: ${data.method}: ${data.args}`);
                let {id, method, args} = data;
                if (cb[method])
                    cb[method](id, args);
            });
        });
        let naive = {
            method: (name, fn) => {
                ready.then(() => {
                    cb[name] = async (id, args) => {
                        let result = await fn({}, args);
                        await post(`${baseServer}/verb/${id}/`, {verb: 'finish', args: result});
                    };
                });
            }
        };
        naives[naiveName] = {ready, naive};
        return naive;
    };
    return {genNaive,ServerAgent,getAgent,interactCallServer};
}
let {ServerAgent,getAgent,interactCallServer}= initNaive();

(async function test() {
    rl.question(`\n输入方法名进入流程,默认为helloWorld方法:`,async answer => {
        let res = await ServerAgent.invoke(answer||`helloWorld`,{});
        if (res.naive){
            helloNaive = res.naive;
            rl.question(`\n输入要测试的naive方法,若方法名为helloWorld,默认有input,redirect,popup,store,retrieve,info,finish方法:`,async answer =>{
                await getAgent(helloNaive).invoke(answer||"info",{});
            })
        }
    });
})();